internal IBaseView {
	protected attributes String.Dict = [:]
	protected subitems = []  // infered to Any.Array

	construct(id String = none) {
		if id !== none {
			this.attributes['id'] = id
		}
	}

	add_subitem(item) {
		this.subitems[] = item
	}

	set_attribute(key String, value String) {
		if not /^[a-z][a-z0-9]*$/i.test(key) {
			throw Exception("Invalid key '$key'")
		}

		this.attributes[key] = value
	}

	build_attributes() {
		items = []
		for key, value in this.attributes {
			items[] = '$key="#{value}"'
		}

		return items.join("\n")
	}

	render() XView {
		return <view ${this.build_attributes()}>
			${this.subitems.join("\n")}
		</view>
	}

	to_string() String {
		return this.render()
	}
}

public ListView: IBaseView {
	get_subviews() {
		return this.subitems.map((item) => {
			return <li>#{item}</li>
		})
	}

	render() XView {
		return <ul ${this.build_attributes()}>
			${this.get_subviews().join("\n\t")}
		</ul>
	}
}

list = ListView('demo')
list.add_subitem('A simple text title')
list.add_subitem('A title has HTML chars <x>')
print list
